package io.kiki.sba.registry.server.clientInterface.strategy.impl;

import io.kiki.sba.registry.common.model.appmeta.InterfaceMapping;
import io.kiki.sba.registry.common.model.client.pb.AppList;
import io.kiki.sba.registry.common.model.client.pb.GetRevisionsResponse;
import io.kiki.sba.registry.common.model.client.pb.MetaHeartbeatResponse;
import io.kiki.sba.registry.common.model.client.pb.ServiceAppMappingResponse;
import io.kiki.sba.registry.common.model.constants.ValueConstants;
import io.kiki.sba.registry.common.model.store.AppRevision;
import io.kiki.sba.registry.core.model.RegisterResponse;
import io.kiki.sba.registry.server.clientInterface.converter.pb.AppRevisionConvertor;
import io.kiki.sba.registry.server.clientInterface.metadata.MetadataCacheRegistry;
import io.kiki.sba.registry.server.clientInterface.push.PushSwitchService;
import io.kiki.sba.registry.server.clientInterface.strategy.AppRevisionHandlerStrategy;
import io.kiki.sba.registry.util.StringFormatter;
import io.kiki.sba.registry.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

public class DefaultAppRevisionHandlerStrategy implements AppRevisionHandlerStrategy {

    private static final Logger logger = LoggerFactory.getLogger(DefaultAppRevisionHandlerStrategy.class);


    @Autowired
    private MetadataCacheRegistry metadataCacheRegistry;

    @Autowired
    private PushSwitchService pushSwitchService;

    protected void beforeRegister(AppRevision appRevision) {
    }

    @Override
    public void handleAppRevisionRegister(AppRevision appRevision, RegisterResponse registerResponse, String remoteAddress) {
        try {
            validate(appRevision);
            beforeRegister(appRevision);
            metadataCacheRegistry.register(appRevision);
            registerResponse.setSuccess(true);
            registerResponse.setMessage("app revision register success!");
        } catch (Throwable e) {
            registerResponse.setSuccess(false);
            String msg = StringFormatter.format("app revision register failed! {}", e.getMessage());
            registerResponse.setMessage(msg);
            logger.error(msg, e);
        } finally {
            logger.info("{},app={},revision={},size={},addr={}", registerResponse.isSuccess() ? "Y" : "N", appRevision.getAppName(), appRevision.getRevision(), appRevision.getSize(), remoteAddress);
        }
    }

    @Override
    public ServiceAppMappingResponse queryApps(List<String> services, String remoteIp) {
        StringUtil.checkNotEmpty(services, "services");
        ServiceAppMappingResponse.Builder builder = ServiceAppMappingResponse.newBuilder();

        if (!pushSwitchService.canIpPushLocal(remoteIp)) {
            builder.setStatusCode(ValueConstants.METADATA_STATUS_DATA_NOT_FOUND);
            return builder.build();
        }

        int statusCode = ValueConstants.METADATA_STATUS_PROCESS_SUCCESS;
        try {
            for (String service : services) {
                InterfaceMapping interfaceMapping = metadataCacheRegistry.getAppNames(service);
                AppList.Builder build = AppList.newBuilder().addAllApps(interfaceMapping.getApps());
                build.setVersion(interfaceMapping.getNanosVersion());
                builder.putServiceAppMapping(service, build.build());
            }
        } catch (Throwable e) {
            statusCode = ValueConstants.METADATA_STATUS_PROCESS_ERROR;
            String msg = StringFormatter.format("query apps by services error. service: {}, {}", services, e.getMessage());
            builder.setMessage(msg);
            logger.error(msg, e);
        }
        builder.setStatusCode(statusCode);
        return builder.build();
    }

    @Override
    public GetRevisionsResponse queryRevision(List<String> revisions) {
        StringUtil.checkNotEmpty(revisions, "revisions");
        GetRevisionsResponse.Builder builder = GetRevisionsResponse.newBuilder();
        int statusCode = ValueConstants.METADATA_STATUS_PROCESS_SUCCESS;
        String queryRevision = null;
        try {
            for (String revision : revisions) {
                queryRevision = revision;
                AppRevision appRevision = null;
                try {
                    appRevision = metadataCacheRegistry.getRevision(revision);
                } catch (Throwable e) {
                    logger.error("query revision {} error", queryRevision, e);
                }
                if (appRevision == null) {
                    statusCode = ValueConstants.METADATA_STATUS_DATA_NOT_FOUND;
                    String msg = StringFormatter.format("query revision not found, {}", revision);
                    builder.setMessage(msg);
                    logger.error(msg);
                } else {
                    builder.putRevisions(revision, AppRevisionConvertor.convert2Pb(appRevision));
                }
            }
        } catch (Throwable e) {
            statusCode = ValueConstants.METADATA_STATUS_PROCESS_ERROR;
            String msg = StringFormatter.format("query revision {} error : {}", queryRevision, e.getMessage());
            builder.setMessage(msg);
            logger.error(msg, e);
        }
        builder.setStatusCode(statusCode);
        return builder.build();
    }

    @Override
    public MetaHeartbeatResponse heartbeat(List<String> revisions) {
        StringUtil.checkNotEmpty(revisions, "revisions");
        MetaHeartbeatResponse.Builder builder = MetaHeartbeatResponse.newBuilder();
        int statusCode = ValueConstants.METADATA_STATUS_PROCESS_SUCCESS;
        for (String revision : revisions) {
            // avoid the error break the heartbeat of next revisions
            try {
                boolean success = metadataCacheRegistry.heartbeat(revision);
                if (!success) {
                    statusCode = ValueConstants.METADATA_STATUS_DATA_NOT_FOUND;
                    String msg = StringFormatter.format("heartbeat revision not found, {}", revision);
                    builder.setMessage(msg);
                    logger.error(msg);
                }
            } catch (Throwable e) {
                statusCode = ValueConstants.METADATA_STATUS_PROCESS_ERROR;
                String msg = StringFormatter.format("revisions {} heartbeat error: {}", revision, e.getMessage());
                builder.setMessage(msg);
                logger.error(msg, e);
            }
        }
        builder.setStatusCode(statusCode);
        return builder.build();
    }

    private void validate(AppRevision appRevision) {
        StringUtil.checkNotBlank(appRevision.getAppName(), "appRevision.appName");
        StringUtil.checkNotBlank(appRevision.getRevision(), "appRevision.revision");
    }


    public DefaultAppRevisionHandlerStrategy setMetadataCacheRegistry(MetadataCacheRegistry metadataCacheRegistry) {
        this.metadataCacheRegistry = metadataCacheRegistry;
        return this;
    }


    public DefaultAppRevisionHandlerStrategy setPushSwitchService(PushSwitchService pushSwitchService) {
        this.pushSwitchService = pushSwitchService;
        return this;
    }
}
